package com.atguigu.tingshu.album.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.AlbumStatMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackStatMapper;
import com.atguigu.tingshu.album.service.TrackInfoService;
import com.atguigu.tingshu.album.service.VodService;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.AlbumStat;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.album.TrackStat;
import com.atguigu.tingshu.model.payment.PaymentInfo;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.album.*;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo> implements TrackInfoService {

    @Autowired
    private TrackInfoMapper trackInfoMapper;

    @Autowired
    private AlbumInfoMapper albumInfoMapper;

    @Autowired
    private VodService vodService;

    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private AlbumStatMapper albumStatMapper;


    /**
     * 保存声音信息
     *
     * @param trackInfoVo
     * @param userId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveTrackInfo(TrackInfoVo trackInfoVo, Long userId) {

        String suggest = vodService.scanText(trackInfoVo.getTrackTitle() + trackInfoVo.getTrackIntro());
        if ("block".equals(suggest) || "review".equals(suggest)) {
            throw new RuntimeException("内容含有敏感信息，请修改");
        }

        //1.根据专辑ID查询专辑信息
        Long albumId = trackInfoVo.getAlbumId();
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        Assert.notNull(albumInfo, "专辑:{}不存在", albumId);

        //2.保存声音信息
        TrackInfo trackInfo = BeanUtil.copyProperties(trackInfoVo, TrackInfo.class);
        //2.1 设置用户ID
        trackInfo.setUserId(userId);
        //2.2 设置声音序号
        trackInfo.setOrderNum(albumInfo.getIncludeTrackCount() + 1);
        //2.3 设置声音来源
        trackInfo.setSource(SystemConstant.TRACK_SOURCE_USER);
        //2.4 设置审核状态： 0501-审核通过 0502"-审核不通过 "0503"-审核中
        trackInfo.setStatus(SystemConstant.TRACK_STATUS_NO_PASS);
        //2.5 TODO 调用腾讯点播平台-获取音频文件时长、大小、类型
        TrackMediaInfoVo trackMediaInfoVo = vodService.getMediaInfo(trackInfo.getMediaFileId());
        if (trackMediaInfoVo != null) {
            trackInfo.setMediaDuration(BigDecimal.valueOf(trackMediaInfoVo.getDuration()));
            trackInfo.setMediaType(trackMediaInfoVo.getType());
            trackInfo.setMediaSize(trackMediaInfoVo.getSize());
        }
        //2.6 判断是否提交封面图片
        if (StringUtils.isBlank(trackInfo.getCoverUrl())) {
            //没有提交，就采用专辑封面图片
            trackInfo.setCoverUrl(albumInfo.getCoverUrl());
        }
        trackInfoMapper.insert(trackInfo);

        //3.更新专辑表（包含声音数量）
        albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() + 1);
        albumInfoMapper.updateById(albumInfo);

        //4.保存声音统计记录
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PLAY, 0);
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COLLECT, 0);
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PRAISE, 0);
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COMMENT, 0);

        //5.TODO 调用腾讯点播平台-发起音频内容异步审核任务
        //5.1 发起异步审核任务，获取审核任务ID
        String reviewTaskId = vodService.startAuditReviewTask(trackInfo.getMediaFileId());
        if (StringUtils.isNotBlank(reviewTaskId)) {
            //5.2 将审核任务ID关联到声音表中，后续采用定时任务查询审核任务结果
            trackInfo.setReviewTaskId(reviewTaskId);
            //5.3 将声音审核状态：审核中
            trackInfo.setStatus(SystemConstant.TRACK_STATUS_REVIEWING);
            trackInfoMapper.updateById(trackInfo);
        }
    }

    @Autowired
    private TrackStatMapper trackStatMapper;

    /**
     * 保存声音统计信息
     *
     * @param trackId
     * @param statType 统计类型：0701-播放量 0702-收藏量 0703-点赞量 0704-评论数
     * @param statNum
     */
    @Override
    public void saveTrackStat(Long trackId, String statType, int statNum) {
        TrackStat trackStat = new TrackStat();
        trackStat.setTrackId(trackId);
        trackStat.setStatType(statType);
        trackStat.setStatNum(statNum);
        trackStatMapper.insert(trackStat);
    }

    /**
     * TODO 该接口必须登录访问
     * 分页查询当前用户声音列表
     *
     * @param pageInfo 分页对象
     * @param query    查询条件
     * @return 分页对象
     */
    @Override
    public Page<TrackListVo> getUserAlbumPage(Page<TrackListVo> pageInfo, TrackInfoQuery query) {
        return trackInfoMapper.getUserAlbumPage(pageInfo, query);
    }

    /**
     * 更新声音信息
     *
     * @param trackInfo
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTrackInfo(TrackInfo trackInfo) {
        String suggest = vodService.scanText(trackInfo.getTrackTitle() + trackInfo.getTrackIntro());
        if ("block".equals(suggest) || "review".equals(suggest)) {
            throw new RuntimeException("内容含有敏感信息，请修改");
        }

        //1.根据声音ID查询原有声音信息
        TrackInfo trackInfoOld = trackInfoMapper.selectById(trackInfo.getId());
        //2.判断声音标识是否需要更新
        if (!trackInfoOld.getMediaFileId().equals(trackInfo.getMediaFileId())) {
            //2.1 如果声音发生变更，则再次获取最新音频信息
            TrackMediaInfoVo mediaInfo = vodService.getMediaInfo(trackInfo.getMediaFileId());
            if (mediaInfo != null) {
                trackInfo.setMediaDuration(BigDecimal.valueOf(mediaInfo.getDuration()));
                trackInfo.setMediaSize(mediaInfo.getSize());
                trackInfo.setMediaType(mediaInfo.getType());
                //2.3 对修改后音频文件发起审核任务
                String reviewTaskId = vodService.startAuditReviewTask(trackInfo.getMediaFileId());
                trackInfo.setStatus(SystemConstant.TRACK_STATUS_REVIEWING);
                trackInfo.setReviewTaskId(reviewTaskId);
            }
            //2.2 删除原有音频文件，避免占用存储空间
            vodService.deleteMedia(trackInfoOld.getMediaFileId());
        }
        //3.更新声音信息
        trackInfoMapper.updateById(trackInfo);
    }

    /**
     * 删除声音
     *
     * @param id
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeTrackInfo(Long id) {
        //1.根据声音ID查询声音记录 得到所属专辑ID
        TrackInfo trackInfo = trackInfoMapper.selectById(id);
        //2.根据专辑ID查询专辑记录
        AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfo.getAlbumId());
        albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() - 1);
        albumInfoMapper.updateById(albumInfo);

        //3.更新声音表序号，将大于（被删除）声音序号的其他声音记录减一
        LambdaUpdateWrapper<TrackInfo> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(TrackInfo::getAlbumId, trackInfo.getAlbumId());
        updateWrapper.gt(TrackInfo::getOrderNum, trackInfo.getOrderNum());
        updateWrapper.setSql("order_num = order_num - 1");
        trackInfoMapper.update(null, updateWrapper);

        //4.删除声音记录
        trackInfoMapper.deleteById(id);

        //5.删除声音统计记录
        trackStatMapper.delete(new LambdaQueryWrapper<TrackStat>().eq(TrackStat::getTrackId, id));

        //6.删除云点播平台音频文件
        vodService.deleteMedia(trackInfo.getMediaFileId());
    }


    /**
     * 分页查询专辑包含声音列表-动态展示付费标识
     *
     * @param pageInfo 分页对象
     * @param albumId  专辑ID
     * @param userId   用户ID（可能为空）
     * @return
     */
    @Override
    public Page<AlbumTrackListVo> getAlbumTrackPage(Page<AlbumTrackListVo> pageInfo, Long albumId, Long userId) {
        //1.根据查询条件分页获取声音列表 AlbumTrackListVo对象默认 isShowPaidMark=false 默认不显示付费标识
        pageInfo = trackInfoMapper.getAlbumTrackPage(pageInfo, albumId);
        //2.TODO 动态判断是否显示付费标识 如果需要展示：将isShowPaidMark设置为true
        //3.根据专辑ID查询专辑信息，得到专辑付费类型已经试听声音数量
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        //3.1 获取专辑付费类型 0101-免费、0102-vip免费、0103-付费
        String payType = albumInfo.getPayType();
        //3.2 获取试听声音数量
        Integer tracksForFree = albumInfo.getTracksForFree();

        //4.判断当前用户是否登录-处理未登录的情况-付费类型属于：VIP免费或付费
        if (userId == null) {
            if (SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(payType) || SystemConstant.ALBUM_PAY_TYPE_REQUIRE.equals(payType)) {
                //4.1 情况一，遍历声音列表，将试听声音排除掉，将其他声音付费标识为true
                pageInfo.getRecords()
                        .stream()
                        .filter(t -> t.getOrderNum() > tracksForFree)
                        .forEach(t -> t.setIsShowPaidMark(true));
            }
        } else {
            //5. 处理已登录情况。根据用户购买情况判断是否显示付费标识
            //5.1 远程调用用户服务获取用户信息，得到用户身份
            UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(userId).getData();
            Assert.notNull(userInfoVo, "用户{}不存在", userId);
            Boolean isVIP = false;
            if (userInfoVo.getIsVip().intValue() == 1 && userInfoVo.getVipExpireTime().after(new Date())) {
                isVIP = true;
            }
            Boolean needCheck = false;
            //5.2 情况二:普通用户收听资源是VIP免费，需要进一步获取用户购买情况进行下一步判断
            if (!isVIP && SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(payType)) {
                needCheck = true;
            }
            //5.3 情况三:所有用户收听资源是付费，需要进一步获取用户购买情况进行下一步判断
            if (SystemConstant.ALBUM_PAY_TYPE_REQUIRE.equals(payType)) {
                needCheck = true;
            }

            //5.4 判断当前页中声音是否需要将付费标识改为true,如果经过查询购买状态未购买则将付费标识改为true
            if (needCheck) {
                //5.4.1 获取下本页中需要检查购买状态声音ID列表（去掉试听声音ID）
                List<Long> needCheckPayStatusTrackIdList = pageInfo.getRecords().stream()
                        .filter(t -> t.getOrderNum() > tracksForFree)
                        .map(AlbumTrackListVo::getTrackId)
                        .collect(Collectors.toList());
                //5.4.2 远程调用用户服务获取本页中声音购买状态
                Map<Long, Integer> payResultMap = userFeignClient.userIsPaidTrack(userId, albumId, needCheckPayStatusTrackIdList).getData();
                //5.4.3 判断本页中声音在购买结果中是0说明未购买，付费标识改为true 试听的去除掉
                //pageInfo.getRecords().stream().filter(t -> t.getOrderNum() > tracksForFree)
                //        .forEach(t -> t.setIsShowPaidMark(payResultMap.get(t.getTrackId()) == 0));
                pageInfo.getRecords()
                        .stream()
                        .filter(t -> t.getOrderNum() > tracksForFree)
                        .forEach(t -> {
                            //当前声音在购买Map中是否等于0 如果是0 将付费标识改为true
                            Integer payStatus = payResultMap.get(t.getTrackId());
                            if (payStatus.intValue() == 0) {
                                t.setIsShowPaidMark(true);
                            }
                        });
            }
        }
        return pageInfo;
    }

    /**
     * 更新数据库中声音表跟专辑表统计信息
     *
     * @param trackStatMqVo
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTrackStat(TrackStatMqVo trackStatMqVo) {
        //1.更新声音表统计信息
        LambdaUpdateWrapper<TrackStat> statLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        statLambdaUpdateWrapper.eq(TrackStat::getTrackId, trackStatMqVo.getTrackId());
        statLambdaUpdateWrapper.eq(TrackStat::getStatType, trackStatMqVo.getStatType());
        //设置set部分 stat_num = stat_num + ?
        statLambdaUpdateWrapper.setSql("stat_num = stat_num + " + trackStatMqVo.getCount());
        trackStatMapper.update(null, statLambdaUpdateWrapper);

        //2.更新所属专辑统计信息：播放量、评论数 需要被同时更新
        if (SystemConstant.TRACK_STAT_PLAY.equals(trackStatMqVo.getStatType())) {
            //更新所属专辑播放量
            this.updateAlbumStat(trackStatMqVo.getAlbumId(), SystemConstant.ALBUM_STAT_PLAY, trackStatMqVo.getCount());
        }
        if (SystemConstant.TRACK_STAT_COMMENT.equals(trackStatMqVo.getStatType())) {
            //更新所属专辑评论量
            this.updateAlbumStat(trackStatMqVo.getAlbumId(), SystemConstant.ALBUM_STAT_COMMENT, trackStatMqVo.getCount());
        }
    }

    /**
     * 获取声音统计信息
     * @param trackId
     * @return
     */
    @Override
    public TrackStatVo getTrackStatVo(Long trackId) {
        return trackInfoMapper.getTrackStatVo(trackId);
    }

    /**
     * 根据待购声音作为起始标准查询当前用户未购买声音列表
     * @param trackId
     * @param trackCount
     * @return
     */
    @Override
    public List<TrackInfo> getwaitPayTrackInfoList(Long userId, Long trackId, Integer trackCount) {
        //1.根据待购声音ID获取声音信息 得到声音序号以及专辑ID
        TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
        Integer orderNum = trackInfo.getOrderNum();
        Long albumId = trackInfo.getAlbumId();

        //2.远程调用用户服务获取该专辑下用户已购声音ID列表
        List<Long> userPaidTrackIdList = userFeignClient.getUserPaidTrackIdList(albumId).getData();

        //3.查询专辑下声音列表，条件：1.专辑ID（等值） 2.声音序号（大于等于） 3.排除已购声音ID（not in） 4.排序（按序号升序） 5.限制购买数量
        LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TrackInfo::getAlbumId, albumId);
        queryWrapper.ge(TrackInfo::getOrderNum, orderNum);
        if(CollectionUtil.isNotEmpty(userPaidTrackIdList)){
            //排除掉已购声音ID
            queryWrapper.notIn(TrackInfo::getId, userPaidTrackIdList);
        }
        //仅查询声音ID、标题、封面、专辑ID（在订单服务中需要获取声音单价需要）
        queryWrapper.select(TrackInfo::getId, TrackInfo::getTrackTitle, TrackInfo::getCoverUrl, TrackInfo::getAlbumId);
        queryWrapper.orderByAsc(TrackInfo::getOrderNum);
        queryWrapper.last("limit "+trackCount);
        return trackInfoMapper.selectList(queryWrapper);
    }

    /**
     * 更新指定专辑统计信息
     * @param albumId 专辑ID
     * @param statType 统计类型
     * @param statNum 增量统计数值
     */
    public void updateAlbumStat(Long albumId, String statType, int statNum){
        LambdaUpdateWrapper<AlbumStat> statLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        statLambdaUpdateWrapper.eq(AlbumStat::getAlbumId, albumId);
        statLambdaUpdateWrapper.eq(AlbumStat::getStatType, statType);
        //设置set部分 stat_num = stat_num + ?
        statLambdaUpdateWrapper.setSql("stat_num = stat_num + " + statNum);
        albumStatMapper.update(null, statLambdaUpdateWrapper);
    }

}
